feat(hosting-cli): add reflex cloud gcp deploy for Cloud Run#6450
Conversation
Fetches a Dockerfile and bash deploy script from flexgen (`GET /api/v1/cli/gcp-cloud-run-manifest`), writes the Dockerfile into the user's source directory, prints the script, and runs it via bash after the user confirms. Pre-flights `bash`/`gcloud`/`docker` on PATH and an active gcloud account, and surfaces a clear message on 403 (Enterprise tier required). Deploy parameters (project, region, service name, AR repo, version) are passed via env vars to the script. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Merging this PR will not alter performance
Comparing Footnotes
|
Greptile SummaryThis PR adds a
Confidence Score: 5/5Safe to merge; the previously flagged environment-passthrough security issue is addressed via an explicit allowlist, and the new deploy flow is well-tested with 12 targeted unit tests. The core deploy logic is sound: pre-flight checks, manifest fetching, Dockerfile-in-heredoc embedding, dollar-sign escaping for Cloud Build substitutions, and tempfile cleanup are all implemented correctly. The only non-trivial concerns are a dead DOCKERFILE_NAME constant and the implicit coupling between the client-side rewrite and the IMAGE variable name in the server script, neither of which represents a current defect on the changed path. packages/reflex-hosting-cli/src/reflex_cli/v2/gcp.py — specifically the _rewrite_builds_submit function's implicit dependency on the server script's IMAGE variable name. Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant CLI as reflex cloud deploy --gcp
participant Preflight as Pre-flight Checks
participant Reflex as Reflex API
participant Bash as bash -s (restricted env)
participant GCP as Google Cloud
User->>CLI: reflex cloud deploy --gcp --gcp-project X
CLI->>Preflight: check bash / gcloud / docker on PATH
CLI->>Preflight: "gcloud auth list --filter=status:ACTIVE"
Preflight-->>CLI: OK / Exit(1)
CLI->>Reflex: GET /api/v1/cli/gcp-cloud-run-manifest (X-API-TOKEN)
Reflex-->>CLI: "{ dockerfile, deploy_command }"
Note over CLI: _build_cloudbuild_yaml(dockerfile) embeds Dockerfile via heredoc
Note over CLI: _rewrite_builds_submit(script) replaces gcloud builds submit
CLI->>User: print rewritten script + env vars
alt interactive mode
User->>CLI: y / n
end
CLI->>CLI: write cloudbuild.yaml to tempfile
CLI->>Bash: bash -s (allowlisted env only)
Bash->>GCP: gcloud services enable ...
Bash->>GCP: gcloud artifacts repositories create ...
Bash->>GCP: "gcloud builds submit --config=cloudbuild.yaml"
GCP->>GCP: Cloud Build writes Dockerfile, builds and pushes image
Bash->>GCP: gcloud run deploy ...
GCP-->>User: Service URL
CLI->>CLI: delete tempfile (always)
Reviews (3): Last reviewed commit: "update docs" | Re-trigger Greptile |
…act constants Address PR feedback: - Restrict the deploy script's environment to an allowlist of host vars (PATH, HOME, gcloud/docker config, proxy/TLS) plus the explicit deploy overrides. Prevents a tampered or compromised flexgen manifest from exfiltrating unrelated host secrets like AWS_*/GITHUB_TOKEN. - Add --interactive/--no-interactive (default true) so the command works in CI. In non-interactive mode the run prompt is skipped, and an existing Dockerfile errors out unless --overwrite-dockerfile is set. - Extract env-var keys and manifest field names into module-level constants per project convention. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…p`, add docs Flatten the `gcp` group into a single `deploy` command with a `--gcp` target flag so the surface can grow to other targets without nesting. `--gcp-project` becomes optional at the Click level and is validated in-function so the missing-target error fires first. Add a hosting doc (with Enterprise-only callout) covering prerequisites, options, what gets created in the GCP project, the env-allowlist security model, CI usage, and troubleshooting. Wire it into the sidebar's Self Hosting section and add `Gcp` -> `GCP` to the sidebar acronym map. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
b3cb739 to
16a39ee
Compare
…p deploy
The Dockerfile no longer touches disk anywhere near the user's source —
it's embedded (base64) as an inline `docker build` step inside a Cloud
Build config written to a tempfile, and the flexgen script's
`gcloud builds submit --tag X .` invocation is rewritten in-memory to
`--config="${REFLEX_CLOUDBUILD_YAML}" --substitutions=_IMAGE="${IMAGE}"`.
The script runs with cwd = the user's source dir, so the user's tree is
the Cloud Build upload context. No temp dir of symlinks, no source-tree
mutation. The cloudbuild.yaml tempfile is removed after the deploy.
If `gcloud builds submit` can't be located in the manifest's script
(format drift on flexgen's side), the rewrite errors out clearly so the
breakage surfaces immediately rather than half-running.
Verified end-to-end against a real GCP project: 1m53s Cloud Build, new
Cloud Run revision deployed and serving traffic, source Dockerfile
timestamp unchanged, tempfile cleaned up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
masenf
left a comment
There was a problem hiding this comment.
we can bring this in, but i think it would be better to have the flexgen service return the cloudbuild.yaml and bake the gcloud scripting pieces into the cli itself, rather than a bash script.
this still gives us control on the server side to run commands in the builder environment, but notably the user is NOT running some shell script from the web. it would also help with cross platformness, as we probably do want to continue supporting windows (and bash/docker is not always available there)
| bash_path = shutil.which("bash") | ||
| if not bash_path: | ||
| console.error( | ||
| "`bash` was not found on PATH; required to run the deploy script." | ||
| ) | ||
| raise click.exceptions.Exit(1) |
There was a problem hiding this comment.
do we actually need bash? this makes it kind of un-fun to run on windows
| if not shutil.which("docker"): | ||
| console.error( | ||
| "The `docker` CLI was not found on PATH; required to build the image." | ||
| ) | ||
| raise click.exceptions.Exit(1) |
There was a problem hiding this comment.
do we actually use docker? i thought the builds were coordinated by gcloud builders
ENG-9469
Summary
reflex cloud gcp deploycommand that fetches a Dockerfile + bash deploy script from flexgen (GET /api/v1/cli/gcp-cloud-run-manifest), writes the Dockerfile into the user's source directory, prints the script, and runs it via bash after a single y/n confirmation.bash/gcloud/dockeron PATH and an active gcloud account, with actionable error messages. Surfaces a clear "Enterprise tier required" message on a 403 from flexgen.--gcp-projectrequired;--region,--service-name,--ar-repo,--version) are passed to the script viaGCP_PROJECT,GCP_REGION,SERVICE_NAME,AR_REPO,VERSIONenvironment variables.--dry-runprints the manifest without writing or executing;--overwrite-dockerfileskips the existing-Dockerfile prompt.Auth model
X-API-Tokenflow (hosting.get_authenticated_client).gcloudlogin (detected viagcloud auth list --filter=status:ACTIVE).Test plan
uv run pytest tests/units/reflex_cli— 152 tests pass (12 new intests/units/reflex_cli/v2/test_gcp.py).uv run ruff check,uv run ruff format,uv run pyrightclean on changed files.uv run pre-commit run --files <changed>passes (ruff-format, ruff-check, codespell, update-pyi-files, pyright).🤖 Generated with Claude Code